package com.ecommerce.onion.domain;

import java.math.BigDecimal;
import java.util.*;

/**
 * @packageName: domain（领域包）
 * @className: Order(订单领域)
 * @description: 围绕此订单领域展开业务
 * @author: luds
 * @version: v1.0
 * @date: 2021-03/25
 */
public class Order {
    /**
     * 订单ID
     */
    private UUID id;
    /**
     * 订单状态（已创建，已完成）
     */
    private OrderStatus status;
    /**
     * 订单项目列表
     */
    private List<OrderItem> orderItems;
    /**
     * 订单价格
     */
    private BigDecimal price;
    /**
     * 默认构造器
     */
    public Order(){}
    /**
     * 通过订单ID及产品构建订单
     * @param id: 订单ID
     * @param product: 产品领域实例
     */
    public Order(final UUID id, final Product product) {
        this.id = id;

        //由产品实例返回一个不变的可序列化的订单项目列表
        this.orderItems = new ArrayList<>(Collections.singletonList(new OrderItem(product)));

        //初始状态为创建
        this.status = OrderStatus.CREATED;

        //采用产品价格作为订单价格
        this.price = product.getPrice();
    }

    /**
     * 完成订单
     */
    public void complete() {
        //验证状态
        validateState();

        //把订单状态更新成：已完成
        this.status = OrderStatus.COMPLETED;
    }

    /**
     * 由产品追加新订单
     * @param product: 产品实例
     */
    public void addOrder(final Product product) {
        //验证状态
        validateState();

        //验证产品
        validateProduct(product);

        //把产品制作的订单追加到订单项目列表
        orderItems.add(new OrderItem(product));
        //追加新订单后，在订单价格基础上+产品价格
        price = price.add(product.getPrice());
    }

    /**
     * 根据ID来移除订单
     * @param id: 订单ID
     */
    public void removeOrder(final UUID id) {
        //验证状态
        validateState();
        //根据订单ID，获取订单项目
        final OrderItem orderItem = getOrderItem(id);
        //从订单项目列表移除此订单项目
        orderItems.remove(orderItem);
        //订单价格也扣除此订单项目的价格
        price = price.subtract(orderItem.getPrice());
    }

    /**
     * 根据订单ID获取订单项目
     * @param id 订单ID
     * @return 订单项目
     */
    private OrderItem getOrderItem(final UUID id) {
        //如果订单项目的产品ID过滤后，返回第一个订单项目；否则抛出异常：此ID的产品不存在。
        return orderItems.stream()
            .filter(orderItem -> orderItem.getProductId()
                .equals(id))
            .findFirst()
            .orElseThrow(() -> new DomainException("Product with " + id + " doesn't exist."));
    }

    /**
     * 验证状态
     */
    private void validateState() {
        //如果订单已经完成，就会抛出异常：这个订单已经完成
        if (OrderStatus.COMPLETED.equals(status)) {
            throw new DomainException("The order is in completed state.");
        }
    }

    /**
     * 验证产品
     * @param product: 产品实例
     */
    private void validateProduct(final Product product) {
        // 如果产品是空的，就会抛出异常：这个产品不能是空的
        if (product == null) {
            throw new DomainException("The product cannot be null.");
        }
    }

    /**
     * 获取订单ID
     * @return 返回订单ID
     */
    public UUID getId() {
        return id;
    }

    /**
     * 获取订单状态
     * @return 返回订单状态
     */
    public OrderStatus getStatus() {
        return status;
    }

    /**
     * 获取订单价格
     * @return 返回价格
     */
    public BigDecimal getPrice() {
        return price;
    }

    /**
     * 获取订单项目列表
     * @return 返回订单项目列表
     */
    public List<OrderItem> getOrderItems() {
        //变不了的列表
        return Collections.unmodifiableList(orderItems);
    }

    /**
     * 由ID、订单项目、价格、订单状态，组合成哈希值
     * @return 返回哈希值
     */
    @Override
    public int hashCode() {
        return Objects.hash(id, orderItems, price, status);
    }

    /**
     * 如果是此订单本身就返回true，如果不是订单就返回false；
     * 除了上述两种情况：如果订单ID、订单项目、价格、状态都相同，就返回true，否则false
     * @param obj: 比较对象
     * @return 比较结果
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!(obj instanceof Order))
            return false;
        Order other = (Order) obj;
        return Objects.equals(id, other.id) && Objects.equals(orderItems, other.orderItems) && Objects.equals(price, other.price) && status == other.status;
    }
}
